package com.yxy.springcloud.framework.unionauth.service;

import com.alibaba.fastjson.JSON;
import com.yxy.springcloud.framework.common.base.BusinessException;
import com.yxy.springcloud.framework.common.redis.RedisUtils;
import com.yxy.springcloud.framework.common.utils.CommonUtils;
import com.yxy.springcloud.framework.common.utils.MD5Util;
import com.yxy.springcloud.framework.unionauth.DTO.AppLoginDto;
import com.yxy.springcloud.framework.unionauth.DTO.ChangePasswordDto;
import com.yxy.springcloud.framework.unionauth.DTO.GetParamValueQueryDTO;
import com.yxy.springcloud.framework.unionauth.dao.*;
import com.yxy.springcloud.framework.unionauth.model.*;
import com.yxy.springcloud.framework.unionauth.utils.TokenCheckUtil;
import com.yxy.springcloud.framework.unionauth.vo.AdminUserExtend;
import com.yxy.springcloud.framework.unionauth.vo.AdminUserInToken;
import com.yxy.springcloud.framework.unionauth.vo.AppLoginUserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author yuxiaoyang
 * app登录 service
 */
@Slf4j
@Service
public class AppLoginService {

  @Resource
  private AdminUserMapper loginUserMapper;

  @Resource
  private AppMapper appMapper;

  @Resource
  private TenantMapper tenantMapper;

  @Resource
  private RoleMapper roleMapper;

  @Resource
  private AppRoleMapper appRoleMapper;

  @Resource
  private UserRoleMapper userRoleMapper;

  @Resource
  private TenantAlluserForbiddenMapper tenantAlluserForbiddenMapper;

  @Autowired
  private AppSystemSettingService appSystemSettingService;

  @Autowired
  private TokenCheckUtil tokenCheckUtil;

  @Autowired
  private HttpServletRequest request;

  @Autowired
  private RedisUtils redisUtils;

  public Map<String, Object> in(AdminUser inputDto, String appName) throws BusinessException {
//    App appObj = new App();
//    appObj.setName(appName);
//    App curApp = appMapper.selectOne(appObj);

    List<AdminUserExtend> adminUserList = loginUserMapper.login(inputDto.getUsername(),
            MD5Util.stringMD5(inputDto.getPassword()).toLowerCase(),
            appName);
    if (adminUserList.size() == 0) {
      log.error("用户名或密码错误 {}", inputDto);
      throw new BusinessException(500, "用户名或密码错误");
    }
    String token = appName + "_token_" + CommonUtils.generateUUID();
    //保存token到set中
    // 需要额外处理adminUserList !!!
    AdminUserInToken tokenVo = new AdminUserInToken();
    AdminUser adminUser = new AdminUser();
    BeanUtils.copyProperties(adminUserList.get(0), adminUser);
    tokenVo.setAdminUser(adminUser);
    for (AdminUserExtend adminUserExtend: adminUserList) {
      Long tenantId = adminUserExtend.getTenantId();
      // 判断该tenantId是否被禁用所有user登录app
      TenantAlluserForbidden queryObj3 = new TenantAlluserForbidden();
      queryObj3.setTenantId(tenantId);
      TenantAlluserForbidden dbObj3 = tenantAlluserForbiddenMapper.selectOne(queryObj3);
      if (dbObj3 != null) {
        log.error("该用户所属的租户已经禁止登录");
        throw new BusinessException(513, "该用户所属的租户已经禁止登录");
      }
      // end
      tokenVo.getTenantIdList().add(tenantId);
      tokenVo.getRoleNameList().add(adminUserExtend.getRoleName());
    }

    // 从app系统设置中读取登录token的有效期, 如果读取不到,则采用默认值 1天
    GetParamValueQueryDTO getParamValueQueryDTO = new GetParamValueQueryDTO();
    getParamValueQueryDTO.setAppCode(appName);
    getParamValueQueryDTO.setParamKey("TOKEN_EXPIRED_TIME");
    String tokenExpiredTime = appSystemSettingService.getParamValue(getParamValueQueryDTO);
    if (StringUtils.isEmpty(tokenExpiredTime)) {// 如果为空, 则采用默认值1天
      tokenCheckUtil.pushToken(token, tokenVo, 60 * 60 * 24);
    } else {
      tokenCheckUtil.pushToken(token, tokenVo, Integer.parseInt(tokenExpiredTime));
    }
    Map<String, Object> map = new HashMap<>();
    map.put("token", token);
    return map;
  }

  public void out() {
    String token = request.getHeader("x-token");
    tokenCheckUtil.deleteToken(token);
  }

  //登录用户修改个人信息
  @Transactional(rollbackFor = Exception.class)
  public void update(AdminUser inputDto) throws BusinessException {
    String userJson = tokenCheckUtil.getUserInfoByToken();
    AdminUser curUser = JSON.parseObject(userJson, AdminUser.class);
    if (!curUser.getId().equals(inputDto.getId())) {
      log.error("用户id不匹配,无法修改信息,请重新登录");
      throw new BusinessException(502, "用户id不匹配,无法修改信息,请重新登录");
    }
    // 修改用户特定信息
    AdminUser updateObj = new AdminUser();
    updateObj.setId(inputDto.getId());
    updateObj.setName(inputDto.getName());
    updateObj.setUsername(inputDto.getUsername());
    updateObj.setPassword(MD5Util.stringMD5(inputDto.getPassword()).toLowerCase());

    updateObj.setUpdateUserId(curUser.getId());
    updateObj.setUpdateUserName(curUser.getUsername());
    updateObj.setUpdateDate(new Date());
    loginUserMapper.updateByPrimaryKeySelective(updateObj);
  }

  // app管理用户登录后紧接着获取用户详情
  public AppLoginUserVO userInfo(String appName) throws BusinessException {
    String userJson = tokenCheckUtil.getUserInfoByToken();
    AdminUserInToken curUser = JSON.parseObject(userJson, AdminUserInToken.class);

    // 获取当前app
    App appQueryObj = new App();
    appQueryObj.setName(appName);
    App curApp = appMapper.selectOne(appQueryObj);

    AppLoginUserVO vo = new AppLoginUserVO();
    BeanUtils.copyProperties(curUser.getAdminUser(), vo);
    for (long tenantId: curUser.getTenantIdList()) {
      Tenant curTenant = tenantMapper.selectByPrimaryKey(tenantId);
      vo.getTenantList().add(curTenant);
    }
    // 设置role
    long userId = curUser.getAdminUser().getId(); // 登录用户id
    UserRole userRoleQueryObj = new UserRole();
    userRoleQueryObj.setUserId(userId);
    List<UserRole> userRoleList = userRoleMapper.select(userRoleQueryObj);

    for (int index = 0; index < userRoleList.size(); index++) {
      UserRole userRole = userRoleList.get(index);
      Long roleId = userRole.getRoleId();
      Role role = roleMapper.selectByPrimaryKey(roleId);
      AppRole appRoleQueryObj = new AppRole();
      appRoleQueryObj.setRoleId(roleId);
      appRoleQueryObj.setAppId(curApp.getId());
      AppRole curAppRole = appRoleMapper.selectOne(appRoleQueryObj);
      if (curAppRole == null) { // 跳过非本项目的approle对应关系
        continue;
      }
      String permissionCodesStr = curAppRole.getPermissionCodes();// 某个角色对应的权限列表 字符串以逗号分隔
      vo.getRoles().add(role.getRoleName());
      vo.getPermissionCodes().addAll(Arrays.asList(permissionCodesStr.split(",")));
    }
    return vo;
  }

  public Map<String, Object> loginWithCheckCode(AppLoginDto inputDto, String appName) throws BusinessException {
    // 首先校验验证码
    String checkCodeKey = inputDto.getCheckCodeKey();
    String checkCodeValue = inputDto.getCheckCodeValue();
    if (StringUtils.isEmpty(checkCodeKey) || StringUtils.isEmpty(checkCodeValue)) {
      throw new BusinessException(510, "验证码不能为空");
    }
    String checkCodeInRedis = redisUtils.get(checkCodeKey);
    if(!checkCodeValue.toLowerCase().equals(checkCodeInRedis)){
      throw new BusinessException(511, "验证码错误");
    }
    //登录成功,redis删除验证码
    redisUtils.del(checkCodeKey);

    AdminUser inputDto2 = new AdminUser();
    BeanUtils.copyProperties(inputDto, inputDto2);
    return in(inputDto2, appName);
  }

  public void changePassword(ChangePasswordDto inputDto) throws BusinessException {
    String oldPassword = inputDto.getOldPassword();
    String newPassword = inputDto.getNewPassword();

    String userJson = tokenCheckUtil.getUserInfoByToken();
    AdminUserInToken curUser = JSON.parseObject(userJson, AdminUserInToken.class);
    long userId = curUser.getAdminUser().getId();
    String oldPasswordMd5 = curUser.getAdminUser().getPassword();
    // 检验旧密码是否正确
    String oldPasswordMd5FromInput = MD5Util.stringMD5(oldPassword).toLowerCase();
    if (!oldPasswordMd5.equals(oldPasswordMd5FromInput)) {
      throw new BusinessException(521, "旧密码不正确");
    }
    // 设置为新密码
    AdminUser updateObj = new AdminUser();
    updateObj.setId(userId);
    updateObj.setPassword(MD5Util.stringMD5(newPassword).toLowerCase());
    updateObj.setUpdateDate(new Date());
    updateObj.setUpdateUserId(userId);
    updateObj.setUpdateUserName(curUser.getAdminUser().getName());
    loginUserMapper.updateByPrimaryKeySelective(updateObj);
  }
}
